import { Injectable, Logger } from '@nestjs/common';
import { ICreateNote, IUpdateNote } from './notification.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from 'libs/database/entities/users.entity';
import { Between, Repository } from 'typeorm';
import { Note } from 'libs/database/entities/note.entity';
import { UserNotes, usersList } from 'libs/interfaces/usersNotes.interface';

@Injectable()
export class NotificationService {
    private readonly logger: Logger = new Logger(NotificationService.name);

    constructor(
        @InjectRepository(User) private usersRepository: Repository<User>,
        @InjectRepository(Note) private noteRepository: Repository<Note>
    ) {}

    async createNote(userTelegramId: string, data: ICreateNote) {
        try {
            this.logger.debug(
                `[notification.createNote] userTelegramId: ${userTelegramId}; data: ${JSON.stringify(data)}`
            );

            const user = await this.usersRepository.findOne({ where: { telegramId: userTelegramId } });
            if (!user) {
                this.logger.warn(`User (${userTelegramId}) not found`);
                return {
                    result: 'User not found',
                };
            }

            const note = await this.noteRepository.save({
                userTelegramId: userTelegramId,
                message: data.message,
                dateNotif: data.dateNotif,
                restartMode: data.restartMode,
            });

            return note;
        } catch (error) {
            this.logger.error(`[notification.createNote] error`, error);
        }
    }

    async getNotesByUser(userTelegramId: string) {
        try {
            this.logger.debug(`[notification.getNotesByUser] userTelegramId: ${userTelegramId}`);

            const user = await this.usersRepository.findOne({ where: { telegramId: userTelegramId } });
            if (!user) {
                this.logger.warn(`User (${userTelegramId}) not found`);
                return {
                    result: 'User not found',
                };
            }
            const notes = await this.noteRepository.find({ where: { userTelegramId: userTelegramId } });

            return notes;
        } catch (error) {
            this.logger.error(`[notification.getNotesByUser] error`, error);
        }
    }

    private controllerUsersNotes(userId: string, usersNotes: usersList, message: string, id: string, date: Date) {
        if (!usersNotes[userId]) {
            const arrayNotes: UserNotes[] = [{ id: id, message: message, date: date }];

            usersNotes[userId] = [...arrayNotes];
            return;
        }

        usersNotes[userId].push({ id: id, message: message, date: date });
    }

    private async getNotesBetweenDates(date: Date, usersNotes: usersList) {
        const startOfDay = new Date(date);
        startOfDay.setHours(3, 0, 0, 0);
        const endOfDay = new Date(date);
        endOfDay.setHours(26, 59, 59, 999);

        const query = await this.noteRepository
            .createQueryBuilder('note')
            .select(['*'])
            .where('note.dateNotif >= :yesterday', { yesterday: startOfDay })
            .andWhere('note.dateNotif < :currentDate', { currentDate: endOfDay });

        const stream = await query.stream();
        stream.on('data', async (note: Note) => {
            this.controllerUsersNotes(note.userTelegramId, usersNotes, note.message, note.id, note.dateNotif);
        });

        const end = new Promise((resolve, reject) => {
            stream.on('end', async () => {
                resolve(1);
            });
            stream.on('error', (error) => {
                reject(error);
            });
        });
        return end;
    }

    async getNotesOnTomorrowByUser(mode: string) {
        try {
            this.logger.debug(`[notification.getNotesOnTomorrowByUser]`);

            const usersNotes: usersList = {};
            const date = new Date();

            if (mode === '1') {
                date.setDate(date.getDate() + 1);
            }

            await this.getNotesBetweenDates(date, usersNotes);

            return usersNotes;
        } catch (error) {
            this.logger.error(`[notification.getNotesOnTomorrowByUser] error`, error);
        }
    }

    async updateNote(userTelegramId: string, noteId: string, data: IUpdateNote) {
        try {
            this.logger.debug(
                `[notification.updateNote] userTelegramId: ${userTelegramId}, noteId: ${noteId}, data: ${JSON.stringify(
                    data
                )}`
            );

            const user = await this.usersRepository.findOne({ where: { telegramId: userTelegramId } });
            if (!user) {
                this.logger.warn(`User (${userTelegramId}) not found`);
                return {
                    result: 'User not found',
                };
            }

            const note = await this.noteRepository.findOne({ where: { id: noteId } });

            note.message = data.message;
            note.dateNotif = data.dateNotif;
            note.restartMode = data.restartMode;

            await this.noteRepository.save(note);

            return note;
        } catch (error) {
            this.logger.error(`[notification.updateNote] error`, error);
        }
    }

    async deleteNote(userTelegramId: string, noteId: string) {
        try {
            this.logger.debug(`[notification.deleteNote] userTelegramId: ${userTelegramId}, noteId: ${noteId}}`);

            const user = await this.usersRepository.findOne({ where: { telegramId: userTelegramId } });
            if (!user) {
                this.logger.warn(`User (${userTelegramId}) not found`);
                return {
                    result: 'User not found',
                };
            }

            const note = await this.noteRepository.delete({ id: noteId });
            if (note.affected === 1) {
                return {
                    result: 'success',
                };
            }

            return {
                result: 'error',
            };
        } catch (error) {
            this.logger.error(`[notification.deleteNote] error`, error);
        }
    }
}
